from .variables import time_counts, TimeFrameType
from datetime import date, datetime
from enum import Enum
from joblib import delayed
import pandas as pd
from uuid import UUID


def safe_delayed(func):
    """包装器，捕获异常并返回 (结果, 错误信息)"""

    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs), None
        except Exception as e:
            return None, str(e)

    return delayed(wrapper)


def align_time_to_before(xdatetime: int, timeframe: TimeFrameType):
    return xdatetime // time_counts[timeframe] // 1000 * time_counts[timeframe] * 1000


def preload_time(xdatetime: int, timeframe: TimeFrameType, num):
    return xdatetime - time_counts[timeframe] * 1000 * num


def get_unfilled_datetime(
    datetime_list: list[int], timeframe: TimeFrameType, start: int, end: int
):
    """
    这个函数根据已有的datetime_list, 会以timeframe的频率查找start到end之间缺失的时间段并返回
    并将缺失的时间段合并成完整的区段
    """

    # Generate full range of datetimes based on the given timeframe
    real_start = align_time_to_before(start, timeframe)
    full_range = list(
        range(int(real_start), int(end + 1), time_counts[timeframe] * 1000)
    )

    # Find missing datetimes
    missing_datetimes = set(full_range).difference(set(datetime_list))
    missing_datetimes = list(sorted(missing_datetimes))
    missing_ranges = []
    if len(missing_datetimes) > 0:
        range_start = missing_datetimes[0]

        for i in range(1, len(missing_datetimes)):
            if (missing_datetimes[i] - missing_datetimes[i - 1]) > time_counts[
                timeframe
            ] * 1000:
                missing_ranges.append(
                    (
                        range_start,
                        missing_datetimes[i - 1] + time_counts[timeframe] * 1000,
                    )
                )
                range_start = missing_datetimes[i]

        missing_ranges.append((range_start, end + time_counts[timeframe] * 1000))

    return missing_ranges


def cut_date_range(
    start: int, end: int, timeframe: TimeFrameType, N: int
) -> list[tuple[int, int]]:
    """
    有重叠端点
    :param N: 每个区间段的数量
    """
    s = start
    e = end
    delta_min = time_counts[timeframe] // 60 * N
    delta_ms = delta_min * 60 * 1000
    p = s
    results = []
    while p < e:
        next_p = p + delta_ms
        if next_p <= e:
            results.append((p, next_p))
        else:
            results.append((p, e))
        p = next_p

    return results


def get_cut_range_from_exist_range(
    datetime_ranges: list[tuple[int, int]], timeframe: TimeFrameType, N: int
) -> list[tuple[int, int]]:
    results = []
    for st, ed in datetime_ranges:
        sp = cut_date_range(st, ed, timeframe, N)
        results.extend(sp)
    return results


def fill_data(
    df: pd.DataFrame,
    start: int,
    end: int,
    timeframe: TimeFrameType,
    time_index: str = "xdatetime",
):
    results = df
    results.set_index(time_index, inplace=True)
    real_start = align_time_to_before(start, timeframe)

    need_idx = list(range(int(real_start), int(end), time_counts[timeframe] * 1000))
    need_idx.name = time_index
    results = results.reindex(need_idx)
    # results.interpolate(method='linear', inplace=True)
    results = results.ffill().bfill()
    results.reset_index(inplace=True)
    return results


escape_chars_map = {
    "\b": "\\b",
    "\f": "\\f",
    "\r": "\\r",
    "\n": "\\n",
    "\t": "\\t",
    "\0": "\\0",
    "\a": "\\a",
    "\v": "\\v",
    "\\": "\\\\",
    "'": "\\'",
}


def escape_param(item):
    if item is None:
        return "NULL"

    elif isinstance(item, datetime):
        return "'%s'" % item.strftime("%Y-%m-%d %H:%M:%S")

    elif isinstance(item, date):
        return "'%s'" % item.strftime("%Y-%m-%d")

    elif isinstance(item, str):
        return "'%s'" % "".join(escape_chars_map.get(c, c) for c in item)

    elif isinstance(item, list):
        return "[%s]" % ", ".join(str(escape_param(x)) for x in item)

    elif isinstance(item, (tuple, set)):
        return "(%s)" % ", ".join(str(escape_param(x)) for x in item)

    elif isinstance(item, Enum):
        return escape_param(item.value)

    elif isinstance(item, UUID):
        return "'%s'" % str(item)

    else:
        return item
